#ifndef CORE_TYPE
#define CORE_TYPE

#define CORE_COUNT 1

#include <map>
#include <array>
#include <mutex>
#include <tuple>
#include <thread>
#include <chrono>
#include <shared_mutex>
#include <unordered_map>

enum STATUS
{
	IntEN = 1,
	Error = 2,
	Trap = 4,
	Masked = 8,
	Privilege = 256,
	Running = 512,
	CacheEn = 1024,
	TLB_En = 2048,
	PageEn = 4096,
	Block = 8192,
	Master = 16384,
	Slave = 32768
};
enum RegVal
{
	Code = 0,
	Status,
	IDTR,
	RvBA,
	RetAddr = 11,
	Stack = 14
};
enum ACCESS
{
	R = 1,
	W = 2,
	X = 4,
	P = 8
};
enum PMETA
{
	RO = 1,
	WR = 2,
	EX = 4,
	PR = 8,
	DIRTY = 16,
	LOADED = 32,
	CACHE_EN = 64,
	ACCESSED = 128
};
enum MESIMETA
{
	Modified = 1,
	Exclusive = 2,
	Share = 4,
	Invalid = 0
};
enum IOTYPE : uint8_t
{
	NOTYPE,
	SEIN = 1,
	SEOUT = 2,
	BLIN = 4,
	BLOUT = 8,
	BLIO = 12,
	TIMER = 16,
	SELF_DEF = 32
};
enum IOSTATUS : uint16_t
{
	DONE = 0,
	READY = 1,
	READ = 2,
	WRITE = 4,
	BUSY = 8,
	SET0 = 0x10, // Set Memory Address(DiskIO)
	SET1 = 0x20, // Set File Address(DiskIO)
	SET2 = 0x40,
	GET0 = 0x100, // Get Memory Address(DiskIO)
	GET1 = 0x200, // Get File Address(DiskIO)
	GET2 = 0x400,
	BAD = 0x800
};
enum ERROR : uint64_t
{
	IllegalInst = uint64_t(0x1c0) << 32,
	AccessFault = (uint64_t)0x1d0 << 32,
	AddrMisAlign = (uint64_t)0x1d1 << 32,
	PageFault = (uint64_t)0x1d2 << 32,
	IllegalPage = (uint64_t)0x1d3 << 32,
	MathError = (uint64_t)0x1e0 << 32,
	Default = (uint64_t)0x1ff << 32
};
enum BRANCH
{
	EQ = 1,
	LT = 2,
	GT = 4,
	ABS = 8
};

struct IntBus
{
	uint16_t port;
	uint16_t cpuid;
	uint16_t intid;
};
struct IOBus
{
	uint64_t data;
	uint16_t port;
	bool direction;
	bool done;
};

template <typename T>
class Atomic
{
private:
	std::shared_mutex mtx;
	T val;

public:
	Atomic() = default;
	Atomic(const T &init_val) : val(init_val) {}
	Atomic &operator=(const Atomic &other)
	{
		this->val = other.val;
		return *this;
	}
	void r_lock() { mtx.lock_shared(); }
	void r_unlock() { mtx.unlock_shared(); }
	void w_lock() { mtx.lock(); }
	void w_unlock() { mtx.unlock(); }
	inline T &get() { return val; }
};

template <>
class Atomic<IOBus *>
{
private:
	std::shared_mutex mtx;
	IOBus *val;

public:
	Atomic() : val() {}
	Atomic(IOBus *init_val) : val(init_val) {}
	Atomic &operator=(const Atomic &other)
	{
		this->val = other.val;
		return *this;
	}
	inline bool is_done()
	{
		std::shared_lock<std::shared_mutex> locker(mtx);
		return val->done;
	}
	inline void clear()
	{
		std::lock_guard<std::shared_mutex> locker(mtx);
		val->done = false;
	}
	inline void setup()
	{
		std::lock_guard<std::shared_mutex> locker(mtx);
		val->done = true;
	}
	inline void set_direction(bool dir) // R:true,W:false
	{
		std::lock_guard<std::shared_mutex> locker(mtx);
		val->direction = dir;
	}
	inline bool get_direction()
	{
		std::shared_lock<std::shared_mutex> locker(mtx);
		return val->direction;
	}
	inline void set_port(uint16_t port)
	{
		std::lock_guard<std::shared_mutex> locker(mtx);
		val->port = port;
	}
	inline uint16_t get_port()
	{
		std::shared_lock<std::shared_mutex> locker(mtx);
		return val->port;
	}
	inline void set_data(uint64_t data, int length = 8)
	{
		std::lock_guard<std::shared_mutex> locker(mtx);
		memcpy(&val->data, &data, length);
	}
	inline uint64_t get_data()
	{
		std::shared_lock<std::shared_mutex> locker(mtx);
		return val->data;
	}
};

#endif